/*
 * File: AdobeGlyphList.java
 * Version: 2.0
 * Initial Creation: May 19, 2010 2:16:43 PM
 */
package com.sun.pdfview.font.ttf;

import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.Vector;

import com.sun.pdfview.ResourceManager;

import net.rim.device.api.io.LineReader;

/**
 * Build an object which provides access to all the Adobe glyph names, using
 * a unicode value, and which can translate a glyph name to one or more
 * unicode values.
 *
# ###################################################################################
# Copyright (c) 1997,1998,2002,2007 Adobe Systems Incorporated
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this documentation file to use, copy, publish, distribute,
# sublicense, and/or sell copies of the documentation, and to permit
# others to do the same, provided that:
# - No modification, editing or other alteration of this document is
# allowed; and
# - The above copyright notice and this permission notice shall be
# included in all copies of the documentation.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this documentation file, to create their own derivative works
# from the content of this document to use, copy, publish, distribute,
# sublicense, and/or sell the derivative works, and to permit others to do
# the same, provided that the derived work is not represented as being a
# copy or version of this document.
#
# Adobe shall not be liable to any party for any loss of revenue or profit
# or for indirect, incidental, special, consequential, or other similar
# damages, whether based on tort (including without limitation negligence
# or strict liability), contract or other legal or equitable grounds even
# if Adobe has been advised or had reason to know of the possibility of
# such damages. The Adobe materials are provided on an "AS IS" basis.
# Adobe specifically disclaims all express, statutory, or implied
# warranties relating to the Adobe materials, including but not limited to
# those concerning merchantability or fitness for a particular purpose or
# non-infringement of any third party rights regarding the Adobe
# materials.
# ###################################################################################
# Name:          Adobe Glyph List
# Table version: 2.0
# Date:          September 20, 2002
#
# See http://partners.adobe.com/asn/developer/typeforum/unicodegn.html
#
# Format: Semicolon-delimited fields:
#            (1) glyph name
#            (2) Unicode scalar value
 *
 * @author tomoke
 */
public class AdobeGlyphList
{
	/** provide a translation from a glyph name to the possible unicode values. */
	private static final long GLYPH_TO_UNICODE_ID = 0x6AAE9EAE44A58DF0L;
    static private Hashtable glyphToUnicodes;
    /** provide a translation from a unicode value to a glyph name. */
    private static final long UNICODE_TO_GLYPH_ID = 0xA64118F5A97DCB2L;
    static private Hashtable unicodeToGlyph;
    /** the loader thread we are reading through. */
    private static final long GLYPH_LOADER_ID = 0x25199A0E9FCF4700L; //This is an odd one to store but what basically will happen is a dead thread will be returned and will do nothing.
    static Thread glyphLoaderThread = null;
    
    static
    {
    	if(glyphToUnicodes == null)
    	{
	    	glyphToUnicodes = (Hashtable)ResourceManager.singletonStorageGet(GLYPH_TO_UNICODE_ID);
	    	if(glyphToUnicodes == null)
	    	{
	    		new AdobeGlyphList();
	    		ResourceManager.singletonStorageSet(GLYPH_TO_UNICODE_ID, glyphToUnicodes);
	    		ResourceManager.singletonStorageSet(UNICODE_TO_GLYPH_ID, unicodeToGlyph);
	    		ResourceManager.singletonStorageSet(GLYPH_LOADER_ID, glyphLoaderThread);
	    	}
	    	else
	    	{
	    		unicodeToGlyph = (Hashtable)ResourceManager.singletonStorageGet(UNICODE_TO_GLYPH_ID);
	    		glyphLoaderThread = (Thread)ResourceManager.singletonStorageGet(GLYPH_LOADER_ID);
	    	}
    	}
    }
    
    /** 
     * <p>private constructor to restrict creation to a singleton.</p>
     * 
     * <p>We initialize by creating the storage and parsing the glyphlist
     * into the tables.</p>
     */
    private AdobeGlyphList()
    {
        glyphToUnicodes = new Hashtable(4500);
        unicodeToGlyph = new Hashtable(4500);
        glyphLoaderThread = new Thread(new Runnable()
        {
            public void run()
            {
            	int index;
            	int index2;
                int[] codes;
                String codeTokens;
                String glyphName;
                String uni;
                Vector unicodes = new Vector();
                
                InputStream istr = ResourceManager.getResource("font.ttf.resource").getStream("glyphlist.txt");
                
                LineReader reader = new LineReader(istr);
                String line = "";
                while (line != null)
                {
                    try
                    {
                    	index2 = 1;
                        unicodes.removeAllElements();
                        line = new String(reader.readLine());
                        if (line == null)
                        {
                            break;
                        }
                        line = line.trim();
                        if (line.length() > 0 && !line.startsWith("#"))
                        {
                            // ignore comment lines
                        	index = line.indexOf(';');
                            glyphName = line.substring(0, index);
                            codeTokens = line.substring(index + 1);
                            while (index2 > 0)
                            {
                            	index = codeTokens.indexOf(' ', index2);
                            	if(index < 0)
                            	{
                            		uni = codeTokens.substring(index2 - 1).trim();
                            	}
                            	else
                            	{
                            		uni = codeTokens.substring(index2 - 1, index).trim();
                            	}
                                unicodes.addElement(uni);
                                index2 = index + 1;
                            }
                            int len = unicodes.size();
                            codes = new int[len];
                            for (int i = 0; i < len; i++)
                            {
                                codes[i] = Integer.parseInt((String)unicodes.elementAt(i), 16);
                                unicodeToGlyph.put(new Integer(codes[i]), glyphName);
                            }
                            glyphToUnicodes.put(glyphName, codes);
                        }
                    }
                    catch (IOException ex)
                    {
                        break;
                    }
                }
            }
        }, "Adobe Glyph Loader Thread");
        //glyphLoaderThread.setDaemon(true);
        glyphLoaderThread.setPriority(Thread.MIN_PRIORITY);
        glyphLoaderThread.start();
    }
    
    /**
     * translate a glyph name into the possible unicode values that it
     * might represent. It is possible to have more than one unicode
     * value for a single glyph name.
     *
     * @param glyphName
     * @return int[]
     */
    public static int[] getUnicodeValues(String glyphName)
    {
        while (glyphLoaderThread != null && glyphLoaderThread.isAlive())
        {
            synchronized (glyphToUnicodes)
            {
                try
                {
                    glyphToUnicodes.wait(250);
                }
                catch (InterruptedException ex)
                {
                    // ignore
                }
            }
        }
        return (int[])glyphToUnicodes.get(glyphName);
    }
    
    /**
     * return a single index for a glyph, though there may be multiples.
     * 
     * @param glyphName
     * @return Integer
     */
    public static Integer getGlyphNameIndex(String glyphName)
    {
        int[] unicodes = getUnicodeValues(glyphName);
        if (unicodes == null)
        {
            return null;
        }
        else
        {
            return new Integer(unicodes[0]);
        }
    }
    
    /**
     * translate a unicode value into a glyph name. It is possible for
     * different unicode values to translate into the same glyph name.
     *
     * @param unicode
     * @return String
     */
    public static String getGlyphName(int unicode)
    {
        while (glyphLoaderThread != null && glyphLoaderThread.isAlive())
        {
            synchronized (glyphToUnicodes)
            {
                try
                {
                    glyphToUnicodes.wait(250);
                }
                catch (InterruptedException ex)
                {
                    // ignore
                }
            }
        }
        return (String)unicodeToGlyph.get(new Integer(unicode));
    }
}
